package ms.jasim.model.editor.views;

import java.util.ArrayList;
import java.util.List;

import ms.jasim.framework.ChartCreatorFactory;
import ms.jasim.framework.EvaluatorResult;
import ms.jasim.framework.EventSimulator;
import ms.jasim.framework.IChartCreator;
import ms.jasim.framework.JasimFramework;
import ms.jasim.framework.EventSimulator.EventHistory;
import ms.jasim.framework.EventSimulator.SimulatedTreeNode;
import ms.jasim.framework.IEvaluator.Info;
import ms.jasim.model.Evaluator;
import ms.jasim.model.JasimModel;
import ms.util.ImageCache;

import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.jfree.chart.JFreeChart;
import org.jfree.experimental.chart.swt.ChartComposite;

import swing2swt.layout.BorderLayout;
import swing2swt.layout.FlowLayout;

public class EventSimulationView extends ViewPart {

	private static final String NO_SIMULATION_RESULT_TO_VIEW = "No simulation result to view";
	private TreeViewer viewer;
	public static final String ID = "ms.jasim.model.editor.views.EventSimulationView"; //$NON-NLS-1$
	private CLabel label;
	private StyledText styledText;
	private ArrayList<SimulatedTreeNode> input;
	private Combo cboEvaluator;
	private Button cmdViewChart;
	private IChartCreator chartCreator;
	private ChartComposite chartFrame;
	private Combo cboChart;
	private Combo cboComponent;
	private Composite chartPanel;
	private Composite chartComposite;
	private SimulationTimelinePanel simulationTimeline;
	private EventSimulationSummaryViewer summaryViewer;

	class NodeInfo {
		public Object info;
		public SimulatedTreeNode node;
		public String label;
		
		public NodeInfo (String label, Object info, SimulatedTreeNode node) {
			this.info = info;
			this.node = node;
			this.label = label;
		}
	}
	
	class SimulationNodeContentProvider implements	ITreeContentProvider {

		@Override
		public Object[] getChildren(Object parentElement) {
			if (parentElement instanceof SimulatedTreeNode) {
				ArrayList<Object> children = new ArrayList<Object>();
				SimulatedTreeNode node = (SimulatedTreeNode) parentElement;
				for (EventHistory entry: node.getEventHistory())
					if (entry.getNode() == node)
						children.add(new NodeInfo("Event", entry, node));
				if (node.getChildren() != null && node.getChildren().size() > 0)
					children.add(new NodeInfo("Branches", null, node));
				return children.toArray();
			}
			else if (parentElement instanceof NodeInfo) {
				NodeInfo info = (NodeInfo) parentElement;
				if (info.info == null)
					return info.node.getChildren().toArray();
			}
			return null;
		}

		@Override
		public Object getParent(Object element) {
			if (element instanceof SimulatedTreeNode)
				return ((SimulatedTreeNode) element).getParent();
			return null;
		}

		@Override
		public boolean hasChildren(Object element) {
			return true;
		}

		@SuppressWarnings("unchecked")
		@Override
		public Object[] getElements(Object inputElement) {
			if (inputElement instanceof List)
				return ((List) inputElement).toArray();
			else if (inputElement instanceof EventSimulator)
				return new Object[] {((EventSimulator) inputElement).getRootNode()};
			else 
				return getChildren(inputElement);
		}

		@Override
		public void dispose() {
			
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			
		}

	}
	
	class NodeColumnLabelProvider extends ColumnLabelProvider {
		@Override
		public Image getImage(Object element) {
			if (element instanceof SimulatedTreeNode)
				return ImageCache.getSolutionView().createImage();
			if (element instanceof NodeInfo) {
				NodeInfo node = (NodeInfo) element;
				if (node.info instanceof EventHistory)
					return ImageCache.getEvent().createImage();
				else
					return ImageCache.getImage("icons/branches.png").createImage();
			}
			return null;
		}

		@Override
		public String getText(Object element) {
			if (element instanceof SimulatedTreeNode) {
				return ((SimulatedTreeNode) element).getLabel();
			}
			else if (element instanceof NodeInfo) {
				NodeInfo info = (NodeInfo) element;
				return info.label + (info.info != null ? " " + info.info.toString() : "");
			}
				
			return "?" + element != null ? element.toString() : "null";
		}
	}
	
	class EvaluatorValueColumnLabelProvider extends ColumnLabelProvider {
		@Override
		public String getText(Object element) {
			if (element instanceof SimulatedTreeNode) {
				SimulatedTreeNode node = (SimulatedTreeNode) element;
				Evaluator info = getSelectedEvaluatorInfo();
				if (info != null) {
					EvaluatorResult evalResult = node.getEvaluatorResult(info, false);
					return Float.toString(evalResult.getValue(getSelectedEvaluatorComponent()));
				}
			}
			return "";
		}
	}
	
	/**
	 * Create contents of the view part
	 * @param parent
	 */
	@Override
	public void createPartControl(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(new BorderLayout(0, 0));
		
		// create the north label
		final Composite composite = new Composite(container, SWT.BORDER);
		composite.setLayoutData(BorderLayout.NORTH);
		composite.setLayout(new BorderLayout(0, 0));

		label = new CLabel(composite, SWT.NONE);
		label.setLayoutData(BorderLayout.NORTH);
		label.setText(NO_SIMULATION_RESULT_TO_VIEW);

		CTabFolder folder = new CTabFolder(container, SWT.NONE);
		folder.setTabPosition(SWT.BOTTOM);
		folder.setLayoutData(BorderLayout.CENTER);
		
		CTabItem detailTab = new CTabItem(folder,SWT.NONE);
		detailTab.setText("Detail");
		
		CTabItem sumTab = new CTabItem(folder, SWT.NONE);
		sumTab.setText("Summary");
		
		// create summary viewer
		summaryViewer = new EventSimulationSummaryViewer(this, folder, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		sumTab.setControl(summaryViewer.getTable());
		
		// create the sashform: left-tree view
		SashForm sash = new SashForm(folder, SWT.HORIZONTAL); 
		sash.setLayoutData(BorderLayout.CENTER);
		
		detailTab.setControl(sash);
		
		folder.showItem(detailTab);
		
		// create view in sash form
		
		viewer = new TreeViewer(sash, SWT.NONE);
		SimulationNodeContentProvider provider = new SimulationNodeContentProvider();
		viewer.setContentProvider(provider);
		viewer.getTree().setLayoutData(BorderLayout.CENTER);
		viewer.getTree().setHeaderVisible(true);
		
		{	// Node Column
			TreeViewerColumn column = new TreeViewerColumn(viewer, SWT.NONE);
			column.setLabelProvider(new NodeColumnLabelProvider());
			column.getColumn().setWidth(300);
			column.getColumn().setText("Node");
		}
		
		{	// Evaluator Value Column
			TreeViewerColumn column = new TreeViewerColumn(viewer, SWT.NONE);
			column.setLabelProvider(new EvaluatorValueColumnLabelProvider());
			column.getColumn().setWidth(50);
			column.getColumn().setText("Value");
		}
		
				
		// create viewer tab folder
		
		CTabFolder tabFolder = new CTabFolder(sash, SWT.NONE);
		tabFolder.setTabPosition(SWT.BOTTOM);
		
		// tab "Simulated Actions"
		CTabItem solTabItem = new CTabItem(tabFolder, SWT.NONE);
		solTabItem.setText("Simulated Actions");
		
		styledText = new StyledText(tabFolder, SWT.V_SCROLL | SWT.H_SCROLL);
		solTabItem.setControl(styledText);
		
		// tab "Action Grant"
		CTabItem grantTabItem = new CTabItem(tabFolder, SWT.NONE);
		grantTabItem.setText("Action Grant");
		Composite grantComposite = new Composite(tabFolder, SWT.NONE);
		grantComposite.setLayout(new BorderLayout(0, 0));
		grantTabItem.setControl(grantComposite);
		
		simulationTimeline = new SimulationTimelinePanel(grantComposite);
		
		// tab "Chart"
		CTabItem chartTabItem = new CTabItem(tabFolder, SWT.NONE);
		chartTabItem.setText("Chart");
		
		chartComposite = new Composite(tabFolder, SWT.NONE);
		chartComposite.setLayout(new BorderLayout(0, 0));
		chartTabItem.setControl(chartComposite);
		
		chartPanel = new Composite(chartComposite, SWT.NONE);
		chartPanel.setLayoutData(BorderLayout.SOUTH);
		
		final FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT);
		flowLayout.setAlignment(FlowLayout.LEFT);
		chartPanel.setLayout(flowLayout);

		this.cboChart = new Combo(chartPanel, SWT.READ_ONLY);
		for (String s: ChartCreatorFactory.getChartCreatorID())
			cboChart.add(s);
		if (cboChart.getItemCount() > 0) 
			cboChart.select(0);
		
		cboEvaluator = new Combo(chartPanel, SWT.READ_ONLY);
		cboEvaluator.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				updateComponentCombo();
			}
		});
	
		this.cboComponent = new Combo(chartPanel, SWT.READ_ONLY);
		
		cmdViewChart = new Button(chartPanel, SWT.NONE);
		cmdViewChart.setEnabled(false);
		cmdViewChart.setText("View Chart");
		cmdViewChart.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				showChart();
			}
		});
		
		chartPanel.layout();
		
		// tab chart Data
		//CTabItem chartDataTabItem = new CTabItem(tabFolder, SWT.NONE);
		//solTabItem.setText("Chart Data");
		
		/// --- end of control creation
		
		tabFolder.setSelection(solTabItem);
		
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {
			
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				TreeSelection sel = (TreeSelection) event.getSelection();
				if (sel.getFirstElement() instanceof SimulatedTreeNode) {
					SimulatedTreeNode node = (SimulatedTreeNode) sel.getFirstElement();
					StringBuilder builder = new StringBuilder();
					builder.append("Solution: " + node.getLabel() + "\r\n");
					Evaluator info = getSelectedEvaluatorInfo();
					if (info != null) {
						builder.append(String.format("Selected Evaluator: %s(%s)\r\n", info.getName(), info.getLabel()));
						builder.append(String.format("\tFull solution assessment       : %s\r\n", node.getEvaluatorResult(info, false)));
						builder.append(String.format("\tAccomplished actions assessment: %s\r\n", node.getEvaluatorResult(info, true)));
					}
					builder.append("---------- SIMULATING LOG --------\r\n");
					builder.append(node.getSimulator().convertTimeSlotsToString());
					styledText.setText(builder.toString());
					
					simulationTimeline.setSimulator(node.getSimulator(), null);
				}
			}
		});
		
		getSite().setSelectionProvider(viewer);
		//
		createActions();
		initializeToolBar();
		initializeMenu();
	}

	/**
	 * Create the actions
	 */
	private void createActions() {
		// Create the actions
	}

	/**
	 * Initialize the tool bar
	 */
	private void initializeToolBar() {
	/*	IToolBarManager toolbarManager = getViewSite().getActionBars()
				.getToolBarManager();*/
	}

	/**
	 * Initialize the menu
	 */
	private void initializeMenu() {
		/*IMenuManager menuManager = getViewSite().getActionBars()
				.getMenuManager();*/
	}

	@Override
	public void setFocus() {
		// Set the focus
	}
	
	public void setSimulation(EventSimulator sim) {
		if (sim != null) {
			viewer.setInput(sim);
			viewer.refresh();
			label.setText(sim.getSolution().getName());
		}
		else 
			clearView();
	}

	private void clearView() {
		viewer.setInput(null);
		label.setText(NO_SIMULATION_RESULT_TO_VIEW);
		viewer.refresh();
	}

	public void setInput(List<EventSimulator> result) {
		this.input = null;
		cboEvaluator.removeAll();
		cmdViewChart.setEnabled(false);
		
		summaryViewer.setInput(result);
		
		if (result != null && result.size() > 0) {
			input = new ArrayList<SimulatedTreeNode>();
			for (EventSimulator sim: result)
				input.add(sim.getRootNode());
			
			// populate evaluator list
			JasimModel model = input.get(0).getSimulator().getModel();
			for (Evaluator eval: model.getEvaluators().getItem()) {
				cboEvaluator.add(eval.getLabel() + " " + eval.getName());
			}
			cboEvaluator.select(0);
			cmdViewChart.setEnabled(true);
			updateComponentCombo();
			
			showChart();
			
			viewer.setInput(input);
			viewer.refresh();
			label.setText(input.size() > 1 ? "Multiple simulation view" : input.get(0).getLabel());
		}
		else 
			clearView();
	}

	protected void showChart() {
		this.chartCreator = ChartCreatorFactory.getChartCreator(cboChart.getText());
		
		if (this.chartCreator != null) {
			JFreeChart chart = chartCreator.createChart(this.input, 
						getSelectedEvaluatorInfo(),
						cboComponent.getText(),
						JasimFramework.getRootContext());
			if (chartFrame == null) {
				chartFrame = new ChartComposite(chartComposite, SWT.NONE, chart, true);
				chartFrame.setLayoutData(BorderLayout.CENTER);
				chartComposite.layout();
				chartComposite.redraw();
			}
			else {
				chartFrame.setChart(chart);
				chartFrame.redraw();
			}
		}
		viewer.refresh();
		summaryViewer.refresh();
	}
	
	public Evaluator getSelectedEvaluatorInfo() {
		int evalIdx = cboEvaluator.getSelectionIndex();
		
		if (evalIdx < 0 || input == null || input.size() == 0)	return null;
		
		JasimModel model = input.get(0).getSimulator().getModel();
		return model.getEvaluators().get(evalIdx);
	}
	
	public String getSelectedEvaluatorComponent() {
		return cboComponent.getText();
	}

 	private void updateComponentCombo() {
		if (input != null  && cboEvaluator.getSelectionIndex() >= 0 ) {
			JasimModel model = input.get(0).getSimulator().getModel();
			Evaluator evaluator = model.getEvaluators().get(cboEvaluator.getSelectionIndex());
			Info info = evaluator.getEvaluatorInfo();
			cboComponent.removeAll();
			if (info != null)
				for (String s: info.Components())
					cboComponent.add(s);
			if (cboComponent.getItemCount() > 0)
				cboComponent.select(0);
		}
		chartPanel.layout();
	}
}
